using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Reflection;
using Type = System.Type;

public class StaticMethodPolymorphEvoker<DelegateType> where DelegateType : System.Delegate {

	string methodName;
	Type[] parameterTypes;
	ParameterModifier[] parameterModifiers;
	DelegateType defaultMethod;

	Type boundType;
	Dictionary<Type,DelegateType> dict = new Dictionary<Type,DelegateType>();

	public StaticMethodPolymorphEvoker(Type type,DelegateType defaultMethod) {
		this.boundType=type;
		this.defaultMethod=defaultMethod;
		methodName=defaultMethod.Method.Name;
		var parameterInfos = defaultMethod.Method.GetParameters();
		parameterTypes=new Type[parameterInfos.Length];
		parameterModifiers=new ParameterModifier[parameterInfos.Length];
		for(int i = 0;i<parameterInfos.Length;i++) {
			parameterTypes[i]=parameterInfos[i].ParameterType;
			parameterModifiers[i]=new ParameterModifier();
		}
	}

	public DelegateType this[Type type] {
		get {
			if(type!=boundType&&!type.IsSubclassOf(boundType)) return null;
			if(dict.ContainsKey(type)) return dict[type];

			Type t = type;
			DelegateType result = null;
			while(t!=typeof(object)) {
				if(t==boundType) {
					result=defaultMethod;
					break;
				}
				MethodInfo method = t.GetMethod(
					methodName,
					BindingFlags.Static|BindingFlags.NonPublic|BindingFlags.Public,
					null,
					parameterTypes,
					new ParameterModifier[0]
				);
				if(method!=null) {
					result=System.Delegate.CreateDelegate(typeof(DelegateType),method) as DelegateType;
					break;
				}
				t=t.BaseType;
			}

			dict.Add(type,result);
			return result;
		}
	}

}